#ifndef __NV_RTC_WRAPPER_H__
#define __NV_RTC_WRAPPER_H__

#include "cuda_to_maca_rtc_adaptor.h"
#include "mc_runtime_api.h"
#include "mcrtc.h"

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

const char *wnvrtcGetErrorString(mcrtcResult result);

mcrtcResult wnvrtcGetNumSupportedArchs(int *numArchs);

mcrtcResult wnvrtcGetSupportedArchs(int *supportedArchs);

mcrtcResult wnvrtcVersion(int *major, int *minor);

mcrtcResult wnvrtcAddNameExpression(mcrtcProgram prog,
                                    const char *const name_expression);

mcrtcResult wnvrtcCompileProgram(mcrtcProgram prog, int numOptions,
                                 const char *const *options);

mcrtcResult wnvrtcCreateProgram(mcrtcProgram *prog, const char *src,
                                const char *name, int numHeaders,
                                const char *const *headers,
                                const char *const *includeNames);

mcrtcResult wnvrtcDestroyProgram(mcrtcProgram *prog);

mcrtcResult wnvrtcGetLoweredName(mcrtcProgram prog,
                                 const char *const name_expression,
                                 const char **lowered_name);

mcrtcResult wnvrtcGetPTX(mcrtcProgram prog, char *ptx);

mcrtcResult wnvrtcGetPTXSize(mcrtcProgram prog, size_t *ptxSizeRet);

mcrtcResult wnvrtcGetNVVM(mcrtcProgram prog, char *nvvm);

mcrtcResult wnvrtcGetNVVMSize(mcrtcProgram prog, size_t *nvvmSizeRet);

mcrtcResult wnvrtcGetCUBIN(mcrtcProgram prog, char *cubin);

mcrtcResult wnvrtcGetCUBINSize(mcrtcProgram prog, size_t *cubinSizeRet);

mcrtcResult wnvrtcGetProgramLog(mcrtcProgram prog, char *log);

mcrtcResult wnvrtcGetProgramLogSize(mcrtcProgram prog, size_t *logSizeRet);

#ifdef __cplusplus
}
#endif /* __cplusplus */

#if MCRTC_GET_TYPE_NAME

#if MCRTC_USE_CXX_ABI || __clang__ || __GNUC__
#include <cstdlib>
#include <cxxabi.h>

#include <string>
#include <typeinfo>

#endif

template <typename T> struct __wnvrtcGetTypeName_helper_t {};

inline mcrtcResult wnvrtcGetTypeName(const std::type_info &tinfo,
                                     std::string *result) {
  const char *name = tinfo.name();
  int status;
  char *undecorated_name = abi::__cxa_demangle(name, 0, 0, &status);
  if (status == 0) {
    *result = undecorated_name;
    free(undecorated_name);
    return MCRTC_SUCCESS;
  }

  return MCRTC_ERROR_INTERNAL_ERROR;
}

template <typename T>
inline mcrtcResult wnvrtcGetTypeName(std::string *result) {
  mcrtcResult res =
      wnvrtcGetTypeName(typeid(__wnvrtcGetTypeName_helper_t<T>), result);

  if (res != MCRTC_SUCCESS) {
    return res;
  }

  std::string repr = *result;
  std::size_t idx = repr.find("__wnvrtcGetTypeName_helper_t");
  idx = (idx != std::string::npos) ? repr.find("<", idx) : idx;
  std::size_t last_idx = repr.find_last_of('>');
  if (idx == std::string::npos || last_idx == std::string::npos) {
    return MCRTC_ERROR_INTERNAL_ERROR;
  }
  ++idx;
  *result = repr.substr(idx, last_idx - idx);
  return MCRTC_SUCCESS;
}

#endif

#endif